#include "TimerFd.h"
#include <unistd.h>
#include <poll.h>
#include <stdio.h>
#include <sys/timerfd.h>

#include <iostream>

using std::cout;
using std::cerr;
using std::endl;

TimerFd::TimerFd(int initSec, int peridocSec, TimerFdCallback &&cb)
: _timerfd(createTimerFd())
, _initSec(initSec)
, _peridocSec(peridocSec)
, _isStarted(false)
, _cb(std::move(cb))//注册回调函数
{

}

TimerFd::~TimerFd()
{
    setTimerFd(0, 0);
    close(_timerfd);
}

//运行与停止
void TimerFd::start()
{
    struct pollfd pfd;
    pfd.fd = _timerfd;
    pfd.events = POLLIN;

    _isStarted = true;

    //需要设置定时器
    setTimerFd(_initSec, _peridocSec);

    while(_isStarted)
    {
        int nready = poll(&pfd, 1, 5000);
        if(-1 == nready && errno == EINTR)
        {
            continue;
        }
        else if(-1 == nready)
        {
            cerr << "-1 == nready" << endl;
            return;
        }
        else if(0 == nready)
        {
            cout << ">>poll timeout" << endl;
        }
        else
        {
            if(pfd.revents & POLLIN)
            {
                //如果另外的线程发了通知，handleRead
                //就会被通知，就可以执行任务
                handleRead();
                if(_cb)
                {
                    _cb();//回调函数的执行
                }
            }
        }
    }
}

void TimerFd::stop()
{
    if(_isStarted)
    {
        _isStarted = false;
        setTimerFd(0, 0);
    }
}

//在该函数中执行read，让A线程执行该函数
void TimerFd::handleRead()
{
    uint64_t one = 1;
    ssize_t ret = read(_timerfd, &one, sizeof(uint64_t));
    if(ret != sizeof(uint64_t))
    {
        perror("read");
        return;
    }
}

int TimerFd::createTimerFd()
{
    int fd = timerfd_create(CLOCK_REALTIME, 0);//TimerFd系统调用创建文件描述符
    if(fd < 0)
    {
        perror("timerfd_create");
        return fd;
    }
    return fd;
}

//设置定时器
void TimerFd::setTimerFd(int initSec, int peridocSec)
{
    struct itimerspec value;
    value.it_value.tv_sec = initSec;//起始时间的秒数
    value.it_value.tv_nsec = 0;

    value.it_interval.tv_sec = peridocSec;//周期性时间的秒数
    value.it_interval.tv_nsec = 0;

    int ret = timerfd_settime(_timerfd, 0, &value, nullptr);
    if(ret)
    {
        perror("timedrfd_settime");
        return;
    }
}
